13
13
import org .elasticsearch .cluster .service .ClusterService ;
14
14
import org .elasticsearch .common .Strings ;
15
15
import org .elasticsearch .common .settings .SecureSetting ;
16
+ import org .elasticsearch .common .settings .SecureString ;
16
17
import org .elasticsearch .common .settings .Setting ;
17
18
import org .elasticsearch .common .xcontent .NamedXContentRegistry ;
18
19
import org .elasticsearch .env .Environment ;
24
25
import org .elasticsearch .repositories .blobstore .BlobStoreRepository ;
25
26
import org .elasticsearch .xpack .core .XPackPlugin ;
26
27
27
- import javax .crypto .SecretKey ;
28
- import javax .crypto .spec .SecretKeySpec ;
29
- import java .io .IOException ;
30
- import java .io .InputStream ;
31
- import java .io .UncheckedIOException ;
32
28
import java .util .Arrays ;
33
29
import java .util .Collections ;
34
30
import java .util .HashMap ;
39
35
public class EncryptedRepositoryPlugin extends Plugin implements RepositoryPlugin {
40
36
static final Logger logger = LogManager .getLogger (EncryptedRepositoryPlugin .class );
41
37
static final String REPOSITORY_TYPE_NAME = "encrypted" ;
42
- static final Setting .AffixSetting <InputStream > KEY_ENCRYPTION_KEY_SETTING = Setting .affixKeySetting ("repository.encrypted." ,
43
- "key" , key -> SecureSetting .secureFile (key , null ));
38
+ static final List <String > SUPPORTED_ENCRYPTED_TYPE_NAMES = Arrays .asList ("fs" , "gcs" , "azure" , "s3" );
39
+ static final Setting .AffixSetting <SecureString > ENCRYPTION_PASSWORD_SETTING = Setting .affixKeySetting ("repository.encrypted." ,
40
+ "password" , key -> SecureSetting .secureString (key , null ));
44
41
static final Setting <String > DELEGATE_TYPE_SETTING = Setting .simpleString ("delegate_type" , "" );
45
- static final Setting <String > KEK_NAME_SETTING = Setting .simpleString ("key_name" , "" );
46
- static final String KEK_CIPHER_ALGO = "AES" ;
47
- static final int KEK_LENGTH_IN_BYTES = 32 ; // 256-bit AES symmetric key
42
+ static final Setting <String > PASSWORD_NAME_SETTING = Setting .simpleString ("password_name" , "" );
48
43
49
44
// "protected" because it is overloaded for tests
50
45
protected XPackLicenseState getLicenseState () { return XPackPlugin .getSharedLicenseState (); }
@@ -59,37 +54,22 @@ public EncryptedRepositoryPlugin() {
59
54
60
55
@ Override
61
56
public List <Setting <?>> getSettings () {
62
- return List .of (KEY_ENCRYPTION_KEY_SETTING );
57
+ return List .of (ENCRYPTION_PASSWORD_SETTING );
63
58
}
64
59
65
60
@ Override
66
61
public Map <String , Repository .Factory > getRepositories (Environment env ,
67
62
NamedXContentRegistry registry ,
68
63
ClusterService clusterService ) {
69
- // store all KEKs from the keystore in memory (because the keystore is not readable later on)
70
- final Map <String , SecretKey > repositoryKEKMapBuilder = new HashMap <>();
71
- for (String KEKName : KEY_ENCRYPTION_KEY_SETTING .getNamespaces (env .settings ())) {
72
- final Setting <InputStream > KEKSetting = KEY_ENCRYPTION_KEY_SETTING .getConcreteSettingForNamespace (KEKName );
73
- final SecretKey KEK ;
74
- byte [] encodedKEKBytes = null ;
75
- try (InputStream KEKInputStream = KEKSetting .get (env .settings ())) {
76
- encodedKEKBytes = KEKInputStream .readAllBytes ();
77
- if (encodedKEKBytes .length != KEK_LENGTH_IN_BYTES ) {
78
- throw new IllegalArgumentException ("Expected a 32 bytes (256 bit) wide AES key, but key ["
79
- + KEKSetting .getKey () + "] is [" + encodedKEKBytes .length + "] bytes wide" );
80
- }
81
- KEK = new SecretKeySpec (encodedKEKBytes , 0 , KEK_LENGTH_IN_BYTES , KEK_CIPHER_ALGO );
82
- } catch (IOException e ) {
83
- throw new UncheckedIOException ("Exception while reading [" + KEKName + "] from the node keystore" , e );
84
- } finally {
85
- if (encodedKEKBytes != null ) {
86
- Arrays .fill (encodedKEKBytes , (byte ) 0 );
87
- }
88
- }
89
- logger .debug (() -> new ParameterizedMessage ("Loaded repository AES key [" + KEKName + "] from the node keystore" ));
90
- repositoryKEKMapBuilder .put (KEKName , KEK );
64
+ // load all the passwords from the keystore in memory because the keystore is not readable when the repository is created
65
+ final Map <String , char []> repositoryPasswordsMapBuilder = new HashMap <>();
66
+ for (String passwordName : ENCRYPTION_PASSWORD_SETTING .getNamespaces (env .settings ())) {
67
+ Setting <SecureString > passwordSetting = ENCRYPTION_PASSWORD_SETTING .getConcreteSettingForNamespace (passwordName );
68
+ SecureString encryptionPassword = passwordSetting .get (env .settings ());
69
+ repositoryPasswordsMapBuilder .put (passwordName , encryptionPassword .getChars ());
70
+ logger .debug (() -> new ParameterizedMessage ("Loaded repository password [" + passwordName + "] from the node keystore" ));
91
71
}
92
- final Map <String , SecretKey > repositoryKEKMap = Map .copyOf (repositoryKEKMapBuilder );
72
+ final Map <String , char []> repositoryPasswordsMap = Map .copyOf (repositoryPasswordsMapBuilder );
93
73
94
74
return Collections .singletonMap (REPOSITORY_TYPE_NAME , new Repository .Factory () {
95
75
@@ -109,23 +89,23 @@ public Repository create(RepositoryMetaData metaData, Function<String, Repositor
109
89
DELEGATE_TYPE_SETTING .getKey () + "] must not be equal to [" + REPOSITORY_TYPE_NAME + "]" );
110
90
}
111
91
final Repository .Factory factory = typeLookup .apply (delegateType );
112
- if (null == factory ) {
113
- throw new IllegalArgumentException ("Unrecognized delegate repository type [" + DELEGATE_TYPE_SETTING .getKey () + "]" );
92
+ if (null == factory || false == SUPPORTED_ENCRYPTED_TYPE_NAMES . contains ( delegateType ) ) {
93
+ throw new IllegalArgumentException ("Unsupported delegate repository type [" + DELEGATE_TYPE_SETTING .getKey () + "]" );
114
94
}
115
- final String repositoryKEKName = KEK_NAME_SETTING .get (metaData .settings ());
116
- if (Strings .hasLength (repositoryKEKName ) == false ) {
117
- throw new IllegalArgumentException ("Repository setting [" + KEK_NAME_SETTING .getKey () + "] must be set" );
95
+ final String repositoryPasswordName = PASSWORD_NAME_SETTING .get (metaData .settings ());
96
+ if (Strings .hasLength (repositoryPasswordName ) == false ) {
97
+ throw new IllegalArgumentException ("Repository setting [" + PASSWORD_NAME_SETTING .getKey () + "] must be set" );
118
98
}
119
- final SecretKey repositoryKEK = repositoryKEKMap .get (repositoryKEKName );
120
- if (repositoryKEK == null ) {
99
+ final char [] repositoryPassword = repositoryPasswordsMap .get (repositoryPasswordName );
100
+ if (repositoryPassword == null ) {
121
101
throw new IllegalArgumentException ("Secure setting [" +
122
- KEY_ENCRYPTION_KEY_SETTING .getConcreteSettingForNamespace (repositoryKEKName ).getKey () + "] must be set" );
102
+ ENCRYPTION_PASSWORD_SETTING .getConcreteSettingForNamespace (repositoryPasswordName ).getKey () + "] must be set" );
123
103
}
124
104
final Repository delegatedRepository = factory .create (new RepositoryMetaData (metaData .name (), delegateType ,
125
105
metaData .settings ()));
126
106
if (false == (delegatedRepository instanceof BlobStoreRepository )
127
107
|| delegatedRepository instanceof EncryptedRepository ) {
128
- throw new IllegalArgumentException ("Unsupported delegate type [" + DELEGATE_TYPE_SETTING .getKey () + "]" );
108
+ throw new IllegalArgumentException ("Unsupported delegate repository type [" + DELEGATE_TYPE_SETTING .getKey () + "]" );
129
109
}
130
110
if (false == getLicenseState ().isEncryptedSnapshotAllowed ()) {
131
111
logger .warn ("Encrypted snapshots are not allowed for the currently installed license." +
@@ -134,7 +114,7 @@ public Repository create(RepositoryMetaData metaData, Function<String, Repositor
134
114
LicenseUtils .newComplianceException ("encrypted snapshots" ));
135
115
}
136
116
return new EncryptedRepository (metaData , registry , clusterService , (BlobStoreRepository ) delegatedRepository ,
137
- () -> getLicenseState (), repositoryKEK );
117
+ () -> getLicenseState (), repositoryPassword );
138
118
}
139
119
});
140
120
}
0 commit comments